package com.csair.seam.infrastructure.log;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.csair.seam.domain.model.entity.Equip;
import com.csair.seam.domain.model.entity.IdleShow;
import com.csair.seam.domain.service.IEquipOrgService;
import com.csair.seam.domain.service.IEquipService;
import com.csair.seam.domain.service.IdleShowService;
import com.csair.seam.infrastructure.utils.IdUtils;
import com.csair.seam.infrastructure.web.BaseController;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.constraints.NotBlank;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Pattern;

@Slf4j
@Aspect
@Component
public class IdleShowAspect extends BaseController {

    @Autowired
    private IEquipService equipService;

    @Autowired
    private IEquipOrgService iEquipOrgService;

    @Autowired
    private IdleShowService idleShowService;
    //切入点描述 这个是controller包的切入点
    @Pointcut("execution(public * com.csair.seam.controller.pc.IdleShowContrlloer.validateIdleShow(..)) " +
            "||execution(public * com.csair.seam.controller.pc.IdleShowContrlloer.insertIdleShow(..))")
    public void validateIdleShow(){}//签名，可以理解成这个切入点的一个名称

    /**
     *  为什么验证信息需要写在 aop类里面
     *     1.因为当下系统里面有3个数据库  ncc nc63 aip
     *     2.在一个service不允许开启多个数据源
     *
     *  数据验证
     *      1.利用springboot validated框架 验证idleShow对象值的有效性验证，
     *              并且错误信息封装到BindingResult里面
     *      2.检查文件file对象
     *          2.1 文件名是否规范
     *          2.2 文件大小是否规范 ---> 文件大小是利用spring boot 配置完成 application-{}
     * point[0] = idleShow 对象值的有效性验证
     * point[1] result   result无效值的错误提示信息
     * point[2] files    图片对象
     * @return
     */
    @Around("validateIdleShow()")
    public Object validateIdleShow(ProceedingJoinPoint point) throws Throwable {
        Object[] args = point.getArgs();
        //参数检查
        BindingResult result = (BindingResult) args[1];
        List<String> errorMessage = new ArrayList<>();
        if(result.hasErrors()){
            for (ObjectError error : result.getAllErrors()) {
                errorMessage.add(error.getDefaultMessage());
            }
        }
        if(!errorMessage.isEmpty()){
            return buildError(new HashSet<>(errorMessage).toString(),null);
        }

        //文件检查
        MultipartFile[] files = (MultipartFile[]) args[2];
        String fileName = checkFile(errorMessage,files);
        //检查eqiupCode是否存在
        IdleShow idleShow = (IdleShow) args[0];
        idleShow.setPicData(fileName);
        checkEquipCodes(idleShow,errorMessage);

        if(!errorMessage.isEmpty()){
            return buildError(new HashSet<>(errorMessage).toString(),null);
        }
        //填充数据
        reviseIdleShow(idleShow);
        Object procceed = point.proceed(args);
        return procceed;
    }

    /**
     * idleShow.isFlag() true 代表有设备编码，进入有设备编码处理逻辑
     * idleShow.isFlag() false 代表无设备编码，进入无设备编码处理逻辑
     * @param idleShow
     */
    private void reviseIdleShow(IdleShow idleShow) {
        if(idleShow.isFlag()){ //有设备编码，每台设备只有一台
            existEquipCode(idleShow);
        }else {
            notExistEquipCode(idleShow);
        }
    }

    /**
     * 有编码设备封装数据
     *  1.得到 idshow最大id数,并封装在idleShow
     *  2.生成发布单号（也可以叫序列号）
     * @param idleShow
     */
    public void existEquipCode(IdleShow idleShow){
        String[] equipCodes = idleShow.getEquipCodes().split(",");
      //String[] equipNums   = idleShow.getEquipNums().split(",");
        //生成序列号
        String showCode = "FB"+IdUtils.getShotId(new Date().getTime()+"");
        //得到Show_id最大数
        int maxShowId = idleShowService.getMaxShowId();

        List<IdleShow> iss = new ArrayList<>();
        for(int i = 0;i<equipCodes.length;i++){
            IdleShow is = new IdleShow(idleShow);
            is.setShowId(++maxShowId);
            is.setCreatTime(new Date());
            is.setCreator("admin");
            is.setShowCode(showCode);
            is.setEquipCodes(equipCodes[i]);
            is.setEquipNum(1);
            iss.add(is);
        }
        idleShow.setEquipCodeList(iss);
    }

    /**
     * 无编码设备封装数据
     *  1.得到 idshow最大id数,并封装在idleShow
     *  2.生成发布单号（也可以叫序列号）
     * @param idleShow
     */
    public void notExistEquipCode(IdleShow idleShow){
        String[] equipCodes = idleShow.getEquipCodes().split(",");
        String[] equipNums   = idleShow.getEquipNums().split(",");
        //生成序列号
        String showCode = "FB"+IdUtils.getShotId(new Date().getTime()+"");
        idleShow.setShowCode(showCode);
        //得到Show_id最大数
        int maxShowId = idleShowService.getMaxShowId();
        List<IdleShow> iss = new ArrayList<>();

        if(equipCodes.length == 0||Objects.isNull(equipCodes)){ //设备是空值
            IdleShow is = new IdleShow(idleShow);
            is.setShowId(++maxShowId);
            is.setCreatTime(new Date());
            is.setCreator("admin");
            is.setShowCode(showCode);
            is.setEquipCodes("");
            is.setEquipNum(1);
            iss.add(is);
        }
        idleShow.setEquipCodeList(iss);
    }
    /**
     * 检查eqiupCodes值的有效性
     *  1. idleShow.isFlag() == false 代表不需要下面到验证，因为是无设备编码
     *       return;
     *  2. 检查idleShow是否为null
     *  2. 检查idleShow.getEquipCodes()是否为null
     *  3. 检查idleShow.getEquipCodes()是否有相同的值
     *         3.1 利用冒泡算法比较两个值是否相等
     *  4. 检查idleShow.getEquipCodes()里面的每个值是否是真实有效的
     *  5. 检查idleShow.getEquipCodes()是否重复发布
     * @param idleShow     对象
     * @param errorMessage 错误提示
     */
    private void checkEquipCodes(IdleShow idleShow, List<String> errorMessage) {
        if(!idleShow.isFlag()){ //无设备编码
            return;
        }
        if(Objects.isNull(idleShow)||StringUtils.isEmpty(idleShow.getEquipCodes())){
             errorMessage.add("【设备编码不能是空】");
             return;
        }
        String equipCodeStr = idleShow.getEquipCodes();
        List<String> equipCodes =Arrays.asList(equipCodeStr.split(","));

        //冒泡算法，找出相同到equipCodes并给出提示
        validateEquallyElement(equipCodes,errorMessage);
        //检查
        String corpCode = idleShow.getCorpCode();
        String orgName  = iEquipOrgService.findOrgNameByCode(corpCode);
        if(StringUtils.equals(orgName,idleShow.getCorpName())){
            errorMessage.add("{填写【写公司名称】和【公司编码查询公司名称】对不上】}");
        }
        String deptCode = idleShow.getDeptCode();
        String deptName  = iEquipOrgService.findOrgNameByCode(deptCode);

        if(StringUtils.equals(deptName,idleShow.getDeptName())){
            errorMessage.add("{填写【部门名称】和【部门编码查询部门名称】对不上】}");
        }

        Set<String> equipCodeSet = new HashSet<>(equipCodes);
        for(String equipCode : equipCodeSet){
            if(equipService.findEquipByCode(equipCode) == null){
                errorMessage.add("{【设备编码->"+ equipCode+":【没有查询到、不是一有效的值】】}");
            }
        }
    }

    /**
     * 验证 equipCodes是否有相同到元素
     *  1.1 利用冒泡算法，找出重复元素
     *  2.1 根据equipCode查询数据库是否已经发布
     * @param equipCodes
     * @param errorMessage
     */
    public void validateEquallyElement(List<String> equipCodes,List<String> errorMessage){
        //冒泡算法
        for(int i = 0;i < equipCodes.size();i++){
            for(int j = i+1;j<equipCodes.size();j++){
                if(StringUtils.equals(equipCodes.get(i),equipCodes.get(j))){
                    errorMessage.add("{设备编码->"+ equipCodes.get(i)+"：有重复的}");
                    break;
                }
            }
        }
        Set<String> equipCodeSet = new HashSet<>(equipCodes);
        for(String equipCode : equipCodeSet){
            if(idleShowService.validEquipCode(equipCode))
                errorMessage.add("{设备编码->"+ equipCode+":【已经发布】】}");
        }
    }

    /**
     * 检查文件名称后缀（jpg,png），文件名称格式，是否有xss攻击问题
     * @param errorMessage
     * @param files
     */
    public String checkFile(List<String> errorMessage,MultipartFile... files){
        if(files == null||files.length == 0){
            return "";
        }
        StringBuffer buffer = new StringBuffer();
        for(MultipartFile file:files){
            String fileName  = file.getOriginalFilename();
            buffer.append(fileName+",");
            checkSuffixeName(fileName,errorMessage);
        }
        return buffer.substring(0,buffer.lastIndexOf(","));
    }

    /**
     * 检查文件格式和文件名称是否违规
     * @param fileName
     * @param errorMessage
     */
    public void checkSuffixeName(String fileName,List<String> errorMessage){
        errorMessage.add(fileName.length() < 28 ? "":"{【"+fileName+":【单个文件名最多28个字符】】}");
        //文件格式检查
        String suffix = fileName.substring(fileName.indexOf("."),fileName.length());
        errorMessage.add(Pattern.matches(".jpg|.png|.JPG|.PNG",suffix)?"":"{【"+fileName+":【文件格式不正确【jpg、png、JPG、PNG】】}");
        String puffix = fileName.substring(0,fileName.indexOf("."));
        errorMessage.add(Pattern.matches("^[a-z0-9A-Z\\u4e00-\\u9fa5]+$",puffix)?"":"{【"+fileName+":文件名字只能是【中文、英文字母、数字】】}");
    }
}
